module iic #(
    parameter ADDR_BYTE_NUM = 2,
    parameter ADDR_OVERFLOW =1

)

 (
    input clk    ,
    input rst_n  ,
   
     (* mark_debug = "true" *)  input[7:0] wdata,
   
     (* mark_debug = "true" *)  input[16-1:0] addr,

    
     (* mark_debug = "true" *) input wr_en,
    
     (* mark_debug = "true" *) input rd_en,
   
     (* mark_debug = "true" *)  input sio_in,
             


     (* mark_debug = "true" *)  output reg[7:0] rdata,
    
     (* mark_debug = "true" *)  output reg rdata_vld,

   
     (* mark_debug = "true" *)   output reg sio_out,
   
     (* mark_debug = "true" *)   output reg rdy,
    
     (* mark_debug = "true" *)  output wire en_read,
     
     (* mark_debug = "true" *) output reg sclk,
      (* mark_debug = "true" *)  input[2:0] dev_location_select_in
                              
   




    
    );

    
    parameter      DATA_W =      8;

    parameter      IDLE   =      0;
    parameter      READ1  =      1;
    parameter      READ2  =      2;
    parameter      WRITE  =      3;
    parameter      WAIT   =      4;

    
 

    reg[7:0]            cnt;
    wire                add_cnt;
    wire                end_cnt;

     (* mark_debug = "true" *)  reg[3:0]     state_c;
    reg[3:0]     state_n;

    wire         sclk_high;
    wire         sclk_low;
    wire         sdat_assign;
    
 (* mark_debug = "true" *)  reg[15:0]  x;
  (* mark_debug = "true" *)  reg[15:0]  cnt_sclk;
   wire    add_cnt_sclk;
  (* mark_debug = "true" *)   wire    end_cnt_sclk;
    (* mark_debug = "true" *)  wire    idle2read1_start;
     (* mark_debug = "true" *) wire   idle2write_start;
     (* mark_debug = "true" *) wire    read12read2_start;
      (* mark_debug = "true" *)wire    read22idle_start;
     (* mark_debug = "true" *) wire    write2wait_start;
      (* mark_debug = "true" *)         wire    wait2idle_start;
   
  
     (* mark_debug = "true" *) reg[37:0]   sdat;
                                wire    en_z;


 (* mark_debug = "true" *)  reg[7:0]  wdata_ff0;




/////////////////////match the at24CXX timing ////////////////////////////////
     (* mark_debug = "true" *)  reg[2:0]                    dev_location_select;



     (* mark_debug = "true" *)  reg[ADDR_BYTE_NUM*8-1:0]        addr_in;
     (* mark_debug = "true" *)  reg[ADDR_BYTE_NUM*8-1:0]        addr_ff0;

always @(posedge clk or negedge rst_n) begin
    if(rst_n==1'b0)begin
        addr_in<=0;     
        dev_location_select<=0;
    end
    else if (ADDR_OVERFLOW==1) begin
        addr_in<= addr[7:0];   
        dev_location_select <={dev_location_select_in[2:1],addr[8]};    
    end
    else if (ADDR_OVERFLOW==2) begin
        addr_in<= addr[7:0];   
        dev_location_select <={dev_location_select_in[2],addr[9:8]};    
    end
    else if (ADDR_OVERFLOW==3) begin
        addr_in<= addr[7:0];   
        dev_location_select <={addr[10:8]};    
    end
    else if(ADDR_OVERFLOW==0)begin
        addr_in<= addr[ADDR_BYTE_NUM*8-1:0];    
        dev_location_select<=dev_location_select_in;
    end
    
end




///////////////////////////////////////////////////////




    (* mark_debug = "true" *) assign  en_read = state_c==READ2&&cnt_sclk>=11-1&&cnt_sclk<=19-1;   //read data time


reg wr_en_ff0;
reg rd_en_ff0;
 (* mark_debug = "true" *)  wire wr_en_posedge;
 (* mark_debug = "true" *)  wire rd_en_posedge;








    always @(posedge clk or negedge rst_n) begin
        if(rst_n==1'b0)begin
            wr_en_ff0<=0;
            rd_en_ff0<=0;
        end
        else begin
            wr_en_ff0<=wr_en;
            rd_en_ff0<=rd_en;
        end
    end


 assign wr_en_posedge = wr_en_ff0==0&&wr_en==1;
 assign rd_en_posedge = rd_en_ff0==0&&rd_en==1;




  always  @(posedge clk or negedge rst_n)begin
      if(rst_n==1'b0)begin
           wdata_ff0<=0;
           addr_ff0<=0;
      end
      else if(wr_en_posedge==1||rd_en_posedge==1) begin
          wdata_ff0<=wdata;
          addr_ff0<=addr_in;
      end
  end

 


  //produce 0.2M clk   200KHZ
    always @(posedge clk or negedge rst_n)begin
        if(!rst_n)begin
            cnt <= 0;
        end
        else if(add_cnt)begin
            if(end_cnt)
                cnt <= 0;
            else
                cnt <= cnt + 1;
        end
    end

    assign add_cnt = state_c!=IDLE;       
    assign end_cnt = add_cnt && cnt== 250-1 ;    //20ns*250clk=5000ns=5us

   
    assign sclk_high = add_cnt==1&&cnt==125-1;  //2.5us
    assign sclk_low =  end_cnt;
 
    assign sdat_assign = add_cnt==1&&cnt==63-1;  //20ns * 63 =1260ns=1.26us

    



  
    always@(posedge clk or negedge rst_n)begin
        if(!rst_n)begin
            state_c <= IDLE;
        end
        else begin
            state_c <= state_n;
        end
    end


    always@(*)begin
        case(state_c)
            IDLE:begin
                if(idle2read1_start)begin
                    state_n = READ1;
                end
                else if(idle2write_start)begin
                    state_n = WRITE;
                end
                else begin
                    state_n = state_c;
                end
            end
            READ1:begin
                if(read12read2_start)begin
                    state_n = READ2;
                end
                else begin
                    state_n = state_c;
                end
            end
            READ2:begin
                if(read22idle_start)begin
                    state_n = IDLE;
                end
                else begin
                    state_n = state_c;
                end
            end
           WRITE:begin
                if(write2wait_start)begin
                    state_n = WAIT;
                end
                else begin
                    state_n = state_c;
                end
            end

            WAIT:begin
                if(wait2idle_start)begin
                    state_n = IDLE;
                end
                else begin
                    state_n = state_c;
                end
            end

            default:begin
                state_n = IDLE;
            end
        endcase
    end
  
    assign idle2read1_start    = state_c==IDLE   && rd_en_posedge==1 ;
    assign idle2write_start    = state_c==IDLE   && wr_en_posedge==1 ;
    assign read12read2_start   = state_c==READ1  && end_cnt_sclk==1 ;
    assign read22idle_start    = state_c==READ2  && end_cnt_sclk==1;

    assign write2wait_start    = state_c==WRITE  && end_cnt_sclk==1;
    assign wait2idle_start     = state_c==WAIT   && end_cnt_sclk==1;

   always @(posedge clk or negedge rst_n)begin
       if(!rst_n)begin
           cnt_sclk <= 0;
       end
       else if(add_cnt_sclk)begin
           if(end_cnt_sclk)
               cnt_sclk <= 0;
           else
               cnt_sclk <= cnt_sclk + 1;
       end
   end

   assign add_cnt_sclk = state_c!=IDLE&&end_cnt==1 ;       
   assign end_cnt_sclk = add_cnt_sclk && cnt_sclk==x-1 ;  


   //design sclk
   always  @(posedge clk or negedge rst_n)begin
       if(rst_n==1'b0)begin
           sclk<=1;
       end
       else if(sclk_high==1)begin
           sclk<=1;
       end
       else if(sclk_low==1&&cnt_sclk<x-2)begin
           sclk<=0;
       end

   end

  

  always  @(*)begin
      if(state_c==WRITE)begin
          x=22+ADDR_BYTE_NUM*8 ;
      end
      else if(state_c==READ1)begin
        x=13+ ADDR_BYTE_NUM*8 ;
      end
      else if(state_c==READ2)begin
        x=21;
      end
      else begin   //wire wait cycle  TWR 5ms max 
        x= 1000;
      end
  end



  always  @(*)begin
      if(state_c==READ1)begin
          sdat={1'b0,4'b1010,dev_location_select, 1'b0,1'b0,addr_ff0,1'b0,1'b0,1'b1};
      end
      else if(state_c==READ2)begin
          sdat={1'b0,4'b1010,dev_location_select, 1'b1,1'b0,8'h0,1'b0,1'b0,1'b1};
      end
      else begin
          sdat= {1'b0,4'b1010,dev_location_select, 1'b0,1'b0,addr_ff0,1'b0,wdata_ff0,1'b0,1'b0,1'b1};
      end

  end




    //write sio_d value 
    always  @(posedge clk or negedge rst_n)begin
        if(rst_n==1'b0)begin
            sio_out<=1;
        end
        else if(sdat_assign==1)begin
            sio_out <= sdat[x-cnt_sclk-1];
        end
    end




 //read sio_d
  always  @(posedge clk or negedge rst_n)begin
      if(rst_n==1'b0)begin
          rdata<=0;
      end
      else if(state_c==READ2&&cnt_sclk>=11-1&&cnt_sclk<=18-1&&end_cnt==1)begin
          rdata[7+10-cnt_sclk]<=sio_in;
      end
  end


  always  @(posedge clk or negedge rst_n)begin
      if(rst_n==1'b0)begin
          rdata_vld<=0;
      end
      else if(state_c==READ2&&cnt_sclk==18-1&&end_cnt==1)begin
          rdata_vld<=1;
      end
      else begin
          rdata_vld<=0;
      end
  end


  always  @(*)begin
      if(state_c!=IDLE)begin
          rdy=0;  //busy
      end
      else begin    //idle free 
          rdy=1;
      end
  end


  
  
  endmodule

